package com.c2b.algorithm.leetcode.hot100;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <a href="https://leetcode.cn/problems/3sum/">三数之和</a>
 * <p>
 * 给你一个整数数组 nums ，判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ，同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
 * 你返回所有和为 0 且不重复的三元组。<br>
 * 注意：答案中不可以包含重复的三元组。
 * </p>
 * <pre>
 * 示例 1：
 *      输入：nums = [-1,0,1,2,-1,-4]
 *      输出：[[-1,-1,2],[-1,0,1]]
 *      解释：
 *          nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
 *          nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
 *          nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
 *      不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
 *      注意，输出的顺序和三元组的顺序并不重要。
 *
 * 示例 2：
 *      输入：nums = [0,1,1]
 *      输出：[]
 *      解释：唯一可能的三元组和不为 0 。
 *
 * 示例 3：
 *      输入：nums = [0,0,0]
 *      输出：[[0,0,0]]
 *      解释：唯一可能的三元组和为 0 。
 * </pre>
 *
 * @author c2b
 * @since 2022/4/29 17:13
 */
public class Hot015ThreeSum {

    /**
     * 排序 + 双指针
     *
     * @param nums 整数数组
     * @return 所有和为 0 且不重复的三元组
     * @author c2b
     * @since 2022/4/29 17:14
     */
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        // 数组为null 或者 数组中元素少于3个
        if (null == nums || nums.length < 3) {
            return res;
        }
        // 排序
        Arrays.sort(nums);
        // 所有数都小于0 或者 所有数都大于0
        if (nums[0] > 0 || nums[nums.length - 1] < 0) {
            return res;
        }
        List<List<Integer>> resultList = new ArrayList<>();
        // 从第一个到倒数第三个
        for (int first = 0; first <= nums.length - 3; first++) {
            // 需要和上一次枚举的数不相等，去重操作。
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // 双指针
            int second = first + 1;
            int third = nums.length - 1;
            // 目标：nums[second]+nums[third]==-nums[first]
            int target = -nums[first];
            while (second < third) {
                int tempSum = nums[second] + nums[third];
                if (tempSum > target) {
                    --third;
                } else if (tempSum < target) {
                    ++second;
                } else {
                    // 找到一组满足条件的三元组
                    resultList.add(Arrays.asList(nums[first], nums[second], nums[third]));
                    // 左指针先向后走一步，判断当前数和前一位置的数是否相等
                    do {
                        ++second;
                    } while (second < third && nums[second] == nums[second - 1]);
                    // 左指针先向前走一步，判断当前数和后一位置的数是否相等
                    do {
                        --third;
                    } while (third > second && nums[third] == nums[third + 1]);
                }
            }
        }
        return resultList;
    }

    public static void main(String[] args) {
        Hot015ThreeSum hot015ThreeSum = new Hot015ThreeSum();
        for (List<Integer> list : hot015ThreeSum.threeSum(new int[]{-2, 0, 1, 1, 2})) {
            for (Integer integer : list) {
                System.out.print(integer + "  ");
            }
            System.out.println();
        }
    }
}
